/**
 * \file Vector3D.h
 * \brief Vector3D definition and declaration class
 * \author Edouard ROGE
 * \date 12 septembre 2010
 */

#ifndef _VECTOR3D_
#define _VECTOR3D_

#include "Point3D.h"
#include "math.h"

namespace Math
{
	namespace M3D
	{
		/**
		* \class Vector3D
		* \brief class with template to instantiate a 3D vector inherit from Point3D<T>
		*/
		template < class T >
		class Vector3D : public Point3D< T >
		{
		public:
			/**
			* \fn Vector3D();
			* \brief Default constructor
			*/
			Vector3D();

			/**
			* \fn Vector3D(T _x, T _y, T _z);
			* \param _x copy _x in x
			* \param _y copy _y in y
			* \param _z copy _z in z
			* \brief Constructor with parameter
			*/
			Vector3D(T _x, T _y, T _z);

			/**
			* \fn Vector3D(const Vector3D<T> & v);
			* \param v vector to copy
			* Copy constructor
			*/
			Vector3D(const Vector3D<T> & v);

			/**
			* \fn Vector3D(const Point3D<T> & p1, const Point3D<T> & p2);
			* \param p1 start of vector
			* \param p2 end of vector
			* constructor
			*/
			Vector3D(const Point3D<T> & p1, const Point3D<T> & p2);

			/**
			* \fn ~Vector3D();
			* \brief Destructor
			*/
			~Vector3D();

			/**
			* \fn T Dot( const Vector3D<T> & _o ) const;
			* \param _o 3d vector
			* \return the dot product between (*this) and _o
			*/
			T Dot(const Vector3D<T> & _o) const;

			/**
			* \fn Vector3D<T> Cross( const Vector3D<T> & _o ) const;
			* \param _o 3d vector
			* \return the cross product between (*this) and _o
			*/
			Vector3D<T> Cross(const Vector3D<T> & _o) const;

			/**
			* \fn T GetLength() const;
			* \return the length of the vector
			*/
			T GetLength() const;

			/**
			* \fn Vector3D<T> Normalize() const;
			* \return the normalized vector
			*/
			Vector3D<T> Normalize() const;

			/**
			* \fn Vector3D<T> operator -() const;
			* \return -(*this)
			*/
			Vector3D<T> operator -() const;

			/**
			* \fn Vector3D<T> & operator = (const Vector3D<T> & _o);
			* \param _o remplace (*this) values by _o values
			* \return (*this) with the new value
			* \brief operator =
			*/
			Vector3D<T> & operator = (const Vector3D<T> & _o);

			/**
			* \fn Vector3D<T> & operator += (const Vector3D<T> & _o);
			* \param _o add _o values with (*this) values
			* \return (*this) with the new value
			* \brief operator +=
			*/
			Vector3D<T> & operator += (const Vector3D<T> & _o);

			/**
			* \fn Vector3D<T> & operator -= (const Vector3D<T> & _o);
			* \param _o subtract _o values to (*this) values
			* \return (*this) with the new value
			* \brief operator -=
			*/
			Vector3D<T> & operator -= (const Vector3D<T> & _o);

			/**
			* \fn Vector3D<T> & operator *= (T _f);
			* \param _f scale (*this) value by _f
			* \return (*this) with the new value
			* \brief operator *=
			*/
			Vector3D<T> & operator *= (T _f);

			/**
			* \fn Vector3D<T> & operator /= (T _f);
			* \param _f scale (*this) value by _f
			* \return (*this) with the new value
			* \brief operator /=
			*/
			Vector3D<T> & operator /= (T _f);

			/**
			* \fn template < class S > friend Vector3D<S> operator + (const Vector3D<S> & _o1, const Vector3D<S> & _o2);
			* \param _o1 3d vector
			* \param _o2 3d vector
			* \return 3d vector
			* \brief operator +
			*/
			template < class S > friend Vector3D<S> operator + (const Vector3D<S> & _o1, const Vector3D<S> & _o2);

			/**
			* \fn template < class S > friend Vector3D<S> operator - (const Point3D<S> & _o1, const Point3D<S> & _o2);
			* \param _o1 3d point (end of vector)
			* \param _o2 3d point (start of vector)
			* \return 3d vector
			* \brief operator -
			*/
			template < class S > friend Vector3D<S> operator - (const Point3D<S> & _o1, const Point3D<S> & _o2);

			/**
			* \fn template < class S > friend Vector3D<S> operator - (const Vector3D<S> & _o1, const Vector3D<S> & _o2);
			* \param _o1 3d vector
			* \param _o2 3d vector
			* \return 3d vector
			* \brief operator -
			*/
			template < class S > friend Vector3D<S> operator - (const Vector3D<S> & _o1, const Vector3D<S> & _o2);

			/**
			* \fn template < class S > friend Vector3D<S> operator * (const Vector3D<S> & _o, S _f);
			* \param _o 3d point
			* \param _f scale value
			* \return 3d vector scaled by _f
			* \brief operator *
			*/
			template < class S > friend Vector3D<S> operator * (const Vector3D<S> & _o, S _f);

			/**
			* \fn template < class S > friend Vector3D<S> operator / (const Vector3D<S> & _o, S _f);
			* \param _o 3d point
			* \param _f scale value
			* \return 3d vector scaled by _f
			* \brief operator /
			*/
			template < class S > friend Vector3D<S> operator / (const Vector3D<S> & _o, S _f);

			/**
			* \fn template < class S > friend S operator * (const Vector3D<S> & _o1, const Vector3D<S> & _o2);
			* \param _o1 3d vector
			* \param _o2 3d vector
			* \return Dot product of _o1 between _o2
			*/
			template < class S > friend S operator * (const Vector3D<S> & _o1, const Vector3D<S> & _o2);

			/**
			* \fn template < class S > friend Vector3D<S> operator ^ (const Vector3D<S> & _o1, const Vector3D<S> & _o2);
			* \param _o1 3d vector
			* \param _o2 3d vector
			* \return Cross product of _o1 between _o2
			*/
			template < class S > friend Vector3D<S> operator ^ (const Vector3D<S> & _o1, const Vector3D<S> & _o2);

			/**
			* \fn template < class S > friend bool operator == (const Vector3D<S> & _o1, const Vector3D<S> & _o2);
			* \param _o1 3d point
			* \param _o2 3d point
			* \return true if _o1 is equal to _o2
			* \brief operator ==
			*/
			template < class S > friend bool operator == (const Vector3D<S> & _o1, const Vector3D<S> & _o2);

			/**
			* \fn template < class S > friend bool operator != (const Vector3D<S> & _o1, const Vector3D<S> & _o2);
			* \param _o1 3d vector
			* \param _o2 3d vector
			* \return true if _o1 is not equal to _o2
			* \brief operator !=
			*/
			template < class S > friend bool operator != (const Vector3D<S> & _o1, const Vector3D<S> & _o2);

		};

		/** Vector 3D of float */
		typedef Vector3D<float>	Vector3f;

		/** Vector 3D of integer */
		typedef Vector3D<int>	Vector3i;

		template < class T >
		Vector3D< T >::Vector3D()
		{
		}

		template < class T >
		Vector3D< T >::Vector3D(T _x, T _y, T _z)
			: Point3D<T>(_x, _y, _z)
		{
			}

		template < class T >
		Vector3D< T >::Vector3D(const Vector3D<T> & v)
			: Point3D<T>(v.x, v.y, v.z)
		{
			}

		template < class T >
		Vector3D< T >::Vector3D(const Point3D<T> & p1, const Point3D<T> & p2)
			: Point3D<T>(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z)
		{
			}

		template < class T >
		Vector3D< T >::~Vector3D()
		{
		}

		template < class T >
		T Vector3D<T>::Dot(const Vector3D<T> & _o) const
		{
			return (this->x * _o.x + this->y * _o.y + this->z * _o.z);
		}

		template < class T >
		Vector3D<T> Vector3D<T>::Cross(const Vector3D<T> & _o) const
		{
			return Vector3D<T>(this->y*_o.z - this->z*_o.y, this->z*_o.x - this->x*_o.z, this->x*_o.y - this->y*_o.x);
		}


		template < class T >
		T Vector3D<T>::GetLength() const
		{
			return sqrt(this->x*this->x + this->y*this->y + this->z*this->z);
		}

		template < class T >
		Vector3D<T> Vector3D<T>::Normalize() const
		{
			return (*this) / GetLength();
		}

		template < class T >
		Vector3D<T> Vector3D< T >::operator -()const
		{
			return Vector3D<T>(-this->x, -this->y, -this->z);
		}

		template < class T >
		Vector3D<T>	& Vector3D< T >::operator = (const Vector3D<T> & _o)
		{
			this->x = _o.x;
			this->y = _o.y;
			this->z = _o.z;
			return (*this);
		}

		template < class T >
		Vector3D<T>	& Vector3D< T >::operator += (const Vector3D<T> & _o)
		{
			this->x += _o.x;
			this->y += _o.y;
			this->z += _o.z;
			return (*this);
		}

		template < class T >
		Vector3D<T>	& Vector3D< T >::operator -= (const Vector3D<T> & _o)
		{
			this->x -= _o.x;
			this->y -= _o.y;
			this->z -= _o.z;
			return (*this);
		}

		template < class T >
		Vector3D<T>	& Vector3D< T >::operator *= (T _f)
		{
			this->x *= _f;
			this->y *= _f;
			this->z *= _f;
			return (*this);
		}

		template < class T >
		Vector3D<T>	& Vector3D< T >::operator /= (T _f)
		{
			//LOG_ASSERT( (_f != 0) , "Divide by 0");
			this->x /= _f;
			this->y /= _f;
			this->z /= _f;
			return (*this);
		}
	}
}

/**
 * \fn template < class S > Math::M3D::Vector3D<S> operator + (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2);
 * \param _o1 3d vector
 * \param _o2 3d vector
 * \return 3d vector
 * \brief operator +
 */
template < class S >
Math::M3D::Vector3D<S>	operator + (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2)
{
	return Vector3D<S>(_o1.x + _o2.x, _o1.y + _o2.y, _o1.z + _o2.z);
}

/**
 * \fn template < class S > Math::M3D::Vector3D<S> operator - (const Math::M3D::Point3D<S> & _o1, const Math::M3D::Point3D<S> & _o2);
 * \param _o1 3d point (end of vector)
 * \param _o2 3d point (start of vector)
 * \return 3d vector
 * \brief operator -
 */
template < class S >
Math::M3D::Vector3D<S>	operator - (const Math::M3D::Point3D<S> & _o1, const Math::M3D::Point3D<S> & _o2)
{
	return Vector3D<S>(_o1.x - _o2.x, _o1.y - _o2.y, _o1.z - _o2.z);
}

/**
 * \fn template < class S > Math::M3D::Vector3D<S> operator - (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2);
 * \param _o1 3d vector
 * \param _o2 3d vector
 * \return 3d vector
 * \brief operator -
 */
template < class S >
Math::M3D::Vector3D<S>	operator - (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2)
{
	return Vector3D<S>(_o1.x - _o2.x, _o1.y - _o2.y, _o1.z - _o2.z);
}

/**
 * \fn template < class S > Vector3D<S> operator * (const Math::M3D::Vector3D<S> & _o, S _f);
 * \param _o 3d point
 * \param _f scale value
 * \return 3d vector scaled by _f
 * \brief operator *
 */
template < class S > 
Math::M3D::Vector3D<S>	operator * (const Math::M3D::Vector3D<S> & _o, S _f)
{
	return Math::M3D::Vector3D<S>(_o.x * _f, _o.y * _f, _o.z * _f);
}

/**
 * \fn template < class S > Math::M3D::Vector3D<S> operator / (const Math::M3D::Vector3D<S> & _o, S _f);
 * \param _o 3d point
 * \param _f scale value
 * \return 3d vector scaled by _f
 * \brief operator /
 */
template < class S > 
Math::M3D::Vector3D<S>	operator / (const Math::M3D::Vector3D<S> & _o, S _f)
{
	//LOG_ASSERT( (_f != 0) , "Divide by 0");
	return Math::M3D::Vector3D<S>(_o.x / _f, _o.y / _f, _o.z / _f);
}

/**
 * \fn template < class S > S operator * (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2);
 * \param _o1 3d vector
 * \param _o2 3d vector
 * \return Dot product of _o1 between _o2
 */
template < class S >
S operator * (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2)
{
	return _o1.Dot( _o2 );
}

/**
 * \fn template < class S > Math::M3D::Vector3D<S> operator ^ (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2);
 * \param _o1 3d vector
 * \param _o2 3d vector
 * \return Cross product of _o1 between _o2
 */
template < class S >
Math::M3D::Vector3D<S> operator ^ (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2)
{
	return _o1.Cross(_o2);
}

/**
 * \fn template < class S > bool operator == (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2);
 * \param _o1 3d point
 * \param _o2 3d point
 * \return true if _o1 is equal to _o2
 * \brief operator ==
 */
template < class S > 
bool operator == (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2)
{
	return (_o1.x == _o2.x && _o1.y == _o2.y && _o1.z == _o2.z);
}

/**
 * \fn template < class S > bool operator != (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2);
 * \param _o1 3d vector
 * \param _o2 3d vector
 * \return true if _o1 is not equal to _o2
 * \brief operator !=
 */
template < class S > 
bool operator != (const Math::M3D::Vector3D<S> & _o1, const Math::M3D::Vector3D<S> & _o2)
{
	return !(_o1==_o2);
}

#endif // _VECTOR3D_

